package com.example.demo.jvm.asm;

import com.example.demo.jvm.ByteUtils;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.springframework.asm.*;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

public class AsmCoreApi {

    public static void main(String[] args) throws IOException, IllegalAccessException, InstantiationException {

//        byte[] bytes = FileUtils.readFileToByteArray(new File("./MyMain.class"));


        ClassReader cr = new ClassReader(MyMain.class.getName());
        //new ClassWriter(ClassWriter.COMPUTE_MAXS）：这种方式会自动计算操作数栈和局部变量表的大小，前提是需要调用一下 visitMaxs 方法来触发计算上述两个值，参数值可以随便指定。
        //new ClassWriter(ClassWriter.COMPUTE_FRAMES)：不仅会计算操作数栈和局部变量表，还会自动计算 StackMapFrames。在 Java 6 之后 JVM 在 class 文件的 Code 属性中引入了
        // StackMapTable 属性，作用是为了提高 JVM 在类型检查的验证过程的效率，里面记录的是一个方法中操作数栈与局部变量区的类型在一些特定位置的状态。
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
        ClassVisitor cv = new ClassVisitor(Opcodes.ASM6, cw) {
            @Override
            public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
                System.out.println("field: " + name);
                if(name.equalsIgnoreCase("a")){
                    value = 100;
                }

                //移除字段
                if ("b".equals(name)) {
                    return null;
                }

                return super.visitField(access, name, desc, signature, value);
            }

            @Override
            public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
                System.out.println("method: " + name);

                //移除方法 test02
                if ("test02".equals(name)) {
                    return null;
                }

                return super.visitMethod(access, name, desc, signature, exceptions);
            }

            @Override
            public void visitEnd() {
                super.visitEnd();

                // 新增字段
                FieldVisitor fv = cv.visitField(Opcodes.ACC_PUBLIC, "xyz", "Ljava/lang/String;", null, null);
                if (fv != null) fv.visitEnd();

                //新增方法 返回 b+100
                MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "test02", "(ILjava/lang/String;)V", null, null);
                mv.visitCode();
                mv.visitVarInsn(Opcodes.ICONST_0, 0);
                mv.visitIntInsn(Opcodes.BIPUSH, 100);
                mv.visitInsn(Opcodes.IADD);
                mv.visitInsn(Opcodes.IRETURN);
                // 触发计算 计算栈 和 局部变量表
                mv.visitMaxs(0, 0);
                if (mv != null) mv.visitEnd();
            }
        };
        cr.accept(cv, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG);
        byte[] bytesModified = cw.toByteArray();
        overwriteClassFile(bytesModified,"src/main/java/com/example/demo/jvm/asm/MyMain.class");

//        ByteArrayClassLoader classLoader = new ByteArrayClassLoader();
//        Class<?> modifiedClass = classLoader.defineClassFromByteArray(bytesModified);
//        MyMain modifiedObject = (MyMain) modifiedClass.newInstance();
//        modifiedObject.test01();
//        System.out.println(modifiedObject.test02());
    }


    //使用byte数组直接替换掉原来的class文件
    public static void overwriteClassFile(byte[] modifiedBytecode, String originalClassFilePath) throws IOException {
        // 将新的字节码保存到一个临时文件
        Path tempFilePath = Files.createTempFile(null, ".class");
        Files.write(tempFilePath, modifiedBytecode);

        // 解析相对路径为绝对路径
        Path currentDirectory = Paths.get("").toAbsolutePath();
        Path originalFilePath = currentDirectory.resolve(originalClassFilePath);

        // 使用 Java 的文件操作 API 覆盖原始的 Class 文件
        Files.copy(tempFilePath, originalFilePath, StandardCopyOption.REPLACE_EXISTING);

        // 删除临时文件
        Files.deleteIfExists(tempFilePath);
    }

}
